package com.wlld.nerveCenter;

import com.wlld.config.RZ;
import com.wlld.entity.Conv;
import com.wlld.function.ReLu;
import com.wlld.function.Tanh;
import com.wlld.i.ActiveFunction;
import com.wlld.nerveEntity.*;

import java.util.ArrayList;
import java.util.List;

/**
 * @param
 * @DATA
 * @Author LiDaPeng
 * @Description yolo 模型管理器
 */
public class YoloManager {
    private int hiddenNerveNub = 12;//全链接隐层神经元个数
    private int outNerveNub;//全链接输出神经元个数
    private int hiddenDepth = 1;//隐层深度
    private List<List<Nerve>> depthNerves = new ArrayList<>();//隐层神经元
    private List<Nerve> outNerves = new ArrayList<>();//输出神经元
    private List<Nerve> softMaxList = new ArrayList<>();//softMax层
    private int convDeep;//卷积深度
    private Conv convR = new Conv();
    private Conv convG = new Conv();
    private Conv convB = new Conv();
    private double convStudyPoint = 0.02;//卷积层学习率
    private double allLineStudyPoint = 0.1;//全链接层学习率
    private ActiveFunction convActiveFunction = new ReLu();
    private ActiveFunction activeFunction = new Tanh();
    private int rzType;//正则化类型，默认不进行正则化
    private double lParam;//正则参数
    private int row;//行宽
    private int column;//列高
    private int xSize;
    private int ySize;
    private int kernLen = 5;//卷积核大小
    private int step = 2;//步长

    public YoloManager(int typeNub, int row, int column, int xSize, int ySize) throws Exception {
        this.outNerveNub = typeNub + 5;
        this.row = row;
        this.column = column;
        this.xSize = xSize;
        this.ySize = ySize;
        int deepXSize = getDeep(xSize);
        int deepYSize = getDeep(ySize);
        if (deepXSize < deepYSize) {
            convDeep = deepXSize;
        } else {
            convDeep = deepYSize;
        }
        //初始化三通道卷积层
        initConvManger(convR);
        initConvManger(convG);
        initConvManger(convB);
        //卷积层构建完毕,构建全链接层
        int sensoryNerveNub = getFeatureSize(convDeep) * 3;
        //构建隐层
        initDepthNerve(step, kernLen, sensoryNerveNub);
        //创建输出层
        List<Nerve> nerveList = depthNerves.get(0);//第一层隐层神经元
        //最后一层隐层神经元啊
        List<Nerve> lastNerveList = depthNerves.get(depthNerves.size() - 1);

        for (int i = 1; i < outNerveNub + 1; i++) {
            OutNerve outNerve = new OutNerve(i, hiddenNerveNub, 0, allLineStudyPoint, true,
                    activeFunction, false, true, rzType, lParam, false, step, kernLen);
            //输出层神经元连接最后一层隐层神经元
            outNerve.connectFather(lastNerveList);
            outNerves.add(outNerve);
        }
        //最后一层隐层神经元 与输出神经元进行连接
        for (Nerve nerve : lastNerveList) {
            nerve.connect(outNerves);
        }
        //卷积层输出层与第一层隐层进行链接
        List<Nerve> outNerveList = new ArrayList<>();
        OutNerve outNerveR = convR.getConvOutNerve();
        OutNerve outNerveG = convG.getConvOutNerve();
        OutNerve outNerveB = convB.getConvOutNerve();
        outNerveList.add(outNerveR);
        outNerveList.add(outNerveG);
        outNerveList.add(outNerveB);
        outNerveR.connect(nerveList);
        outNerveG.connect(nerveList);
        outNerveB.connect(nerveList);
        //第一层隐层与卷积输出层进行链接
        for (Nerve nerve : nerveList) {
            nerve.connectFather(outNerveList);
        }
    }

    private void initConvManger(Conv conv) throws Exception {
        OutNerve convOutNerve = new OutNerve(1, 1, hiddenNerveNub, convStudyPoint, true,
                convActiveFunction, true, true, RZ.NOT_RZ, 0, false, step, kernLen);
        conv.setConvOutNerve(convOutNerve);
        //卷积网络神经元
        SensoryNerve sensoryNerve = conv.getSensoryNerve();//卷积输入层
        List<Nerve> convHiddenNerves = conv.getConvHiddenNerves();//隐层神经元
        initConvHiddenNerve(convHiddenNerves);
        List<Nerve> firstConvNerveList = new ArrayList<>();//第一层隐层神经元
        List<Nerve> lastConvNerveList = new ArrayList<>();//最后一层隐层神经元啊
        List<Nerve> outNerveList = new ArrayList<>();//卷积层输出神经元
        outNerveList.add(convOutNerve);
        firstConvNerveList.add(convHiddenNerves.get(0));
        lastConvNerveList.add(convHiddenNerves.get(convHiddenNerves.size() - 1));
        sensoryNerve.connect(firstConvNerveList);
        convHiddenNerves.get(convHiddenNerves.size() - 1).connect(outNerveList);
        convOutNerve.connectFather(lastConvNerveList);
    }

    private void initDepthNerve(int step, int kernLen, int sensoryNerveNub) throws Exception {//初始化隐层神经元1
        for (int i = 0; i < hiddenDepth; i++) {//遍历深度
            List<Nerve> hiddenNerveList = new ArrayList<>();
            double studyPoint = this.allLineStudyPoint;
            if (studyPoint <= 0 || studyPoint > 1) {
                throw new Exception("studyPoint Values range from 0 to 1");
            }
            for (int j = 1; j < hiddenNerveNub + 1; j++) {//遍历同级
                int upNub = 0;
                int downNub = 0;
                if (i == 0) {
                    upNub = sensoryNerveNub;
                } else {
                    upNub = hiddenNerveNub;
                }
                if (i == hiddenDepth - 1) {//最后一层隐层神经元z
                    downNub = outNerveNub;
                } else {
                    downNub = hiddenNerveNub;
                }
                HiddenNerve hiddenNerve = new HiddenNerve(j, i + 1, upNub, downNub, studyPoint, true, activeFunction, false
                        , rzType, lParam, step, kernLen);
                hiddenNerveList.add(hiddenNerve);
            }
            depthNerves.add(hiddenNerveList);
        }
        initHiddenNerve();
    }

    private void initHiddenNerve() {//初始化隐层神经元2
        for (int i = 0; i < hiddenDepth - 1; i++) {//遍历深度
            List<Nerve> hiddenNerveList = depthNerves.get(i);//当前遍历隐层神经元
            List<Nerve> nextHiddenNerveList = depthNerves.get(i + 1);//当前遍历的下一层神经元
            for (Nerve hiddenNerve : hiddenNerveList) {
                hiddenNerve.connect(nextHiddenNerveList);
            }
            for (Nerve nextHiddenNerve : nextHiddenNerveList) {
                nextHiddenNerve.connectFather(hiddenNerveList);
            }
        }
    }

    private void initConvHiddenNerve(List<Nerve> convHiddenNerves) throws Exception {
        //初始化隐层卷积层
        for (int i = 0; i < convDeep; i++) {//遍历深度
            double studyPoint = this.convStudyPoint;
            if (studyPoint <= 0 || studyPoint > 1) {
                throw new Exception("studyPoint Values range from 0 to 1");
            }
            HiddenNerve hiddenNerve = new HiddenNerve(1, i + 1, 1, 1, studyPoint, true, convActiveFunction, true
                    , RZ.NOT_RZ, 0, step, kernLen);
            convHiddenNerves.add(hiddenNerve);
        }
        initConvNerve(convHiddenNerves);

    }

    private void initConvNerve(List<Nerve> convHiddenNerves) {//卷积层构建链接
        for (int i = 0; i < convDeep - 1; i++) {//遍历深度
            List<Nerve> hiddenNerveList = new ArrayList<>();
            List<Nerve> nextHiddenNerveList = new ArrayList<>();
            Nerve hiddenNerve = convHiddenNerves.get(i);//当前遍历隐层神经元
            Nerve nextHiddenNerve = convHiddenNerves.get(i + 1);//当前遍历的下一层神经元
            hiddenNerveList.add(hiddenNerve);
            nextHiddenNerveList.add(nextHiddenNerve);
            hiddenNerve.connect(nextHiddenNerveList);
            nextHiddenNerve.connectFather(hiddenNerveList);
        }
    }

    private int getFeatureSize(int size) {
        int row = this.row;
        int column = this.column;
        for (int i = 0; i < size; i++) {
            row = (row - (kernLen - step)) / step;
            column = (column - (kernLen - step)) / step;
        }
        return row * column;
    }

    private int getDeep(int size) {
        int x = size;
        int convDeep = 0;
        do {
            x = (x - (kernLen - step)) / step;
            if (x > 0) {
                convDeep++;
            }
        } while (x > 0);
        return convDeep;
    }
}
